#!/bin/sh

# Copyright 2016 Dell EMC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Uncomment line below to enable logging
#FLEXREX_DEBUG=1

# Notes:
#  - Please install "jq" package before using this driver.
#  - Please install and configure REX-Ray before using this driver

# stderr
err() {
	debug "failure status" "$*"
	printf "%b" "$*" 1>&2
}

# stdout
log() {
	debug "success status" "$*"
	printf "%b" "$*" >&1
}

# log file
debug() {
	if [ "$FLEXREX_DEBUG" = "1" ]; then
		printf "%s - %s - %s - %s\n" "$(date +"%Y-%m-%d %H:%M:%S")" "$1" "${VOLUMEID}" "$2" >> /var/log/flexrex.log
	fi
}

# detect which binary is available: rexray-client or rexray
if [ -x /usr/bin/rexray-client ]; then
	REXRAY_BIN=/usr/bin/rexray-client
elif [ -x /usr/bin/rexray ]; then
	REXRAY_BIN=/usr/bin/rexray
elif which rexray-client > /dev/null 2>&1; then
	REXRAY_BIN=$(which rexray-client)
elif which rexray > /dev/null 2>&1; then
	REXRAY_BIN=$(which rexray)
else
	err '{ "status": "Failure", "message": "Failed to find rexray binary}"}'
	exit 1
fi

success() {
	log '{"status": "Success"}'
	exit 0
}

usage() {
	err "error: invalid usage\n"
	err "\t$0 init\n"
	err "\t$0 attach <json params>\n"
	err "\t$0 detach <mount device>\n"
	err "\t$0 mount <mount dir> <mount device> <json params> (1.5 only)\n"
	err "\t$0 unmount <mount dir> (1.5 only)\n"
	err "\t$0 waitforattach <mount device> <json params>"
	err "\t$0 mountdevice <mount dir> <mount device> <json params>"
	err "\t$0 unmountdevice <mount dir>"
	err "\t$0 getvolumename <json params>"
	err "\t$0 isattached <json params> <nodename>"
	exit 1
}

ismounted() {
	MOUNT=$(findmnt -n "${MNTPATH}" 2>/dev/null | cut -d' ' -f1)
	if [ "${MOUNT}" = "${MNTPATH}" ]; then echo 1; else echo 0; fi
}

# Requires $JSON_PARAMS to be set
# Sets $VOLUMEID
volidfromjson() {
	VOLUMEID=$(echo "${JSON_PARAMS}" | jq -r '.volumeID')
	if [ -z "${VOLUMEID}" ] || [ "${VOLUMEID}" = null ]; then
		err "{\"status\": \"Failure\", \"message\": \"Unable to extract volumeID\"}"
		exit 1
	fi
}

# Requires $VOLUMEID to be set
# Sets $REX_OUTPUT
getvolinfowithpath() {
	REX_OUTPUT=$(${REXRAY_BIN} volume ls "${VOLUMEID}" --path --format json 2>/dev/null)
}

# Requires $VOLUMEID to be set
# Sets $DEV
getvolumedevice() {
	# Make call to get device info
	getvolinfowithpath
	DEV=$(echo "${REX_OUTPUT}" | jq -r '.[0].attachments[0].deviceName')
	if [ -z "$DEV" ]; then
		debug "getvolumedevice" "failed to get device name - rexray_response: ${REX_OUTPUT}"
		err "{\"status\": \"Failure\", \"message\": \"REX-Ray did not return attached device name\"}"
		exit 1
	fi
	if [ ! -b "${DEV}" ]; then
		err "{\"status\": \"Failure\", \"message\": \"Volume ${VOLUMEID} not present at ${DEV}\"}"
		exit 1
	fi
}

# Requires $VOLUMEID to be set
# Sets $ATTACH_STATUS
getattachstatus() {
	ATTACH_STATUS=$(${REXRAY_BIN} volume ls "${VOLUMEID}" --format json 2>/dev/null | jq '.[0].attachmentState')
	if [ "$FLEXREX_DEBUG" = "1" ]; then
		case "$ATTACH_STATUS" in
			1)
				VOLUME_STATUS_NAME="unknown"
				;;
			2)
				VOLUME_STATUS_NAME="attached"
				;;
			3)
				VOLUME_STATUS_NAME="available"
				;;
			4)
				VOLUME_STATUS_NAME="unavailable"
				;;
		esac
		debug "getattachstatus" "volume status is ${VOLUME_STATUS_NAME}"
	fi
}

attach() {
	JSON_PARAMS=$1
	volidfromjson
	FORCE_ATTACH=$(echo "$1" | jq -r '.forceAttach')
	FORCE_ATTACH_DELAY=$(echo "$1" | jq -r '.forceAttachDelay')
	VOLUME_AVAILABLE=0

	if [ "$FORCE_ATTACH" = "true" ]; then
		if echo "$FORCE_ATTACH_DELAY" | grep '^-\{0,1\}[[:digit:]]\{1,\}$' > /dev/null; then
			STATUS_CHECKS=$(( FORCE_ATTACH_DELAY / 5 ))
			COUNTER=0
			COUNTER_LOGGED=1
			while [ "$COUNTER" -lt "$STATUS_CHECKS" ]; do
				getattachstatus
				if [ "$ATTACH_STATUS" -eq "2" ]; then
					debug "attach" "volume is already attached, skipping attachment"
					VOLUME_ATTACHED=1
					break;
				elif [ "$ATTACH_STATUS" -eq "3" ]; then
					debug "attach" "volume is available, breaking loop"
					VOLUME_AVAILABLE=1
					break;
				fi
				debug "attach" "sleepign for 5 seconds after status check"
				sleep 5
				[ "$(( COUNTER=COUNTER+1 ))" -ne 0 ]
				[ "$(( COUNTER_LOGGED=COUNTER_LOGGED+1 ))" -ne 0 ]
			done
		fi
		if [ "$VOLUME_ATTACHED" = "1" ]; then
			ATTACH_EXIT_CODE=0
		elif [ "$VOLUME_AVAILABLE" = "1" ]; then
			debug "attach" "attaching after volume available"
			OUTPUT=$(${REXRAY_BIN} volume attach "${VOLUMEID}" -i --format json 2>/dev/null)
			ATTACH_EXIT_CODE=$?
			debug "attach" "rexray_attach_response ${OUTPUT}"
		else
			debug "attach" "forcing attach after timer expiration"
			OUTPUT=$(${REXRAY_BIN} volume attach "${VOLUMEID}" -i --force --format json 2>/var/log/rexray.log)
			ATTACH_EXIT_CODE=$?
			debug "attach" "rexray_attach_response ${OUTPUT}"
		fi

	else
		OUTPUT=$(${REXRAY_BIN} volume attach "${VOLUMEID}" -i --format json 2>/dev/null)
		ATTACH_EXIT_CODE=$?
	fi

	if [ "$ATTACH_EXIT_CODE" -ne "0" ]; then
		debug "attach" "nonzero exit code during attach: ${ATTACH_EXIT_CODE}"
		err "{\"status\": \"Failure\", \"message\": \"REX-Ray returned error during attach\"}"
		exit 1
	fi

	#sleep for 2 seconds before checking device path
	sleep 2

	# Make second call to get device info
	getvolumedevice
	log "{\"status\": \"Success\", \"device\":\"${DEV}\"}"
	exit 0
}

waitforattach() {
	EXPECTED_DEV=$1
	JSON_PARAMS=$2
	volidfromjson
	getvolumedevice
	if [ ${EXPECTED_DEV} != ${DEV} ]; then
		debug "waitforattach" "device at unexpected location. found: ${DEV}, expected: ${EXPECTED_DEV}"
		err "{\"status\": \"Failure\", \"message\": \"REX-Ray returned error during attach\"}"
	fi
	log "{\"status\": \"Success\", \"device\":\"${DEV}\"}"
	exit 0
}

isattached() {
	JSON_PARAMS=$1
	volidfromjson
	getattachstatus
	if [ "$ATTACH_STATUS" -eq "3" ]; then
		log "{\"status\": \"Success\", \"attached\":True}"
		exit 0
	fi
	err "{\"status\": \"Failure\", \"message\": \"Volume not attached\", \"attached\":False}"
	exit 1
}

# Requires $VOLUMEID and $VOLUME_PATH to be set
detach() {
	if [ "${VOLUME_PATH}" = "" ]; then
		debug "detach" "detaching volume"
		${REXRAY_BIN} volume detach -i "${VOLUMEID}" >/dev/null 2>&1
		DETACH_EXIT_CODE=$?
		if [ "${DETACH_EXIT_CODE}" -ne "0" ]; then
			err "{\"status\": \"Failure\", \"message\": \"REX-Ray returned error during detach\"}"
			exit 1
		fi
	else
		debug "detach" "volume is currently mounted to ${VOLUME_PATH}"
		err "{\"status\": \"Failure\", \"message\": \"Volume ${VOLUMEID} is currently mounted at ${VOLUME_PATH}\"}"
		exit 1
	fi

	log "{\"status\": \"Success\"}"
	exit 0
}

detachvolume() {
	VOLUMEID=$1
	getattachstatus
	if [ "$ATTACH_STATUS" -ne "2" ]; then
		log "{\"status\": \"Success\"}"
		exit 0
	fi
	getvolinfowithpath
	VOLUME_PATH=$(echo "${REX_OUTPUT}" | jq -r '.[0].Path')

	detach
}

detachdevice() {
	DEV=$1
	if [ ! -b "${DEV}" ]; then
		err "{\"status\": \"Failure\", \"message\": \"Device ${DEV} does not exist\"}"
		exit 1
	fi

	# When only given the device, we have to match on the attachment details from all volumes
	VOLUMES=$(${REXRAY_BIN} volume ls --path --format json 2>/dev/null)
	VOLUME_ATTACHMENT_DETAILS=$(${REXRAY_BIN} volume ls --path --format json | jq -r '[.[] | {name: .name, id: .id, device:.attachments[0].deviceName, path:.Path}] | .[] | select(.device == '\""${DEV}"\"')')
	VOLUMEID=$(echo "${VOLUME_ATTACHMENT_DETAILS}" | jq -r '.id')
	VOLUME_PATH=$(echo "${VOLUME_ATTACHMENT_DETAILS}" | jq -r '.path')

	if [ -z "$VOLUMEID" ]; then
		err "{\"status\": \"Failure\", \"message\": \"Could not find source volume for device ${DEV}\"}"
		exit 1
	fi

	detach
}

domount() {
	MNTPATH=$1
	DEV=$2
	FSTYPE=$(echo "$3" | jq -r '.["kubernetes.io/fsType"]')

	if [ ! -b "${DEV}" ]; then
		err "{\"status\": \"Failure\", \"message\": \"${DEV} does not exist\"}"
		exit 1
	fi

	if [ "$(ismounted)" -eq "1" ] ; then
		success
	fi

	VOLFSTYPE=$(blkid -o udev "${DEV}" 2>/dev/null | grep "ID_FS_TYPE"| cut -d"=" -f2)
	if [ "${VOLFSTYPE}" = "" ]; then
		CMD="mkfs -t ${FSTYPE}"
		if [ "$FSTYPE" = "ext4" ]; then
			CMD="${CMD} -F"
		elif [ "$FSTYPE" = "xfs" ]; then
			CMD="${CMD} -f"
		fi
		if ! ${CMD} "${DEV}" > /dev/null 2>&1; then
			err "{ \"status\": \"Failure\", \"message\": \"Failed to create fs ${FSTYPE} on device ${DEV}\"}"
			exit 1
		fi
	fi

	mkdir -p "${MNTPATH}" > /dev/null 2>&1
	if ! mount "${DEV}" "${MNTPATH}" > /dev/null 2>&1; then
		err "{ \"status\": \"Failure\", \"message\": \"Failed to mount device ${DEV} at ${MNTPATH}\"}"
		exit 1
	fi
	success
}

unmount() {
	MNTPATH=$1
	if [ "$(ismounted)" -eq "0" ] ; then success; fi
	if ! umount "${MNTPATH}" > /dev/null 2>&1; then
		err "{ \"status\": \"Failed\", \"message\": \"Failed to unmount volume at ${MNTPATH}\"}"
		exit 1
	fi
	success
}

getvolumename() {
	JSON_PARAMS=$1
	volidfromjson

	VOLUME_NAME=$(${REXRAY_BIN} volume ls "${VOLUMEID}" --format json 2>/dev/null | jq -r '.[0].id')
	if [ -z "$VOLUME_NAME" ]; then
		err "{\"status\": \"Failure\", \"message\": \"Could not get name of volume ${VOLUME_NAME} via REX-Ray. Does it exist?\"}"
		exit 1
	fi

	log "{\"status\": \"Success\", \"volumeName\":\"${VOLUME_NAME}\"}"
	exit 0
}

op=$1

if [ "$op" = "init" ]; then success; fi
if [ "$#" -lt "2" ]; then usage; fi

shift

debug "$op" "$*"

case "$op" in
	attach)
		attach "$@"
		;;
	detach)
		if [ -e "$1" ]; then
			detachdevice "$@"
		else
			detachvolume "$@"
		fi
		;;
	mount)
		# 3 args is k8s 1.5 style
		if [ "$#" -eq "3" ]; then
			domount "$@"
		fi
		# Defer to k8s default bind-mount since we support mountdevice
		err "{\"status\": \"Not supported\"}"
		;;
	unmount)
		# k8s 1.5 passes in a mount point here
		if [ -e "$1" ]; then
			unmount "$@"
		fi
		# Defer to k8s default bind-mount since we support unmountdevice
		err "{\"status\": \"Not supported\"}"
		;;
	waitforattach)
		waitforattach "$@"
		;;
	mountdevice)
		domount "$@"
		;;
	unmountdevice)
		unmount "$@"
		;;
	getvolumename)
		getvolumename "$@"
		;;
	isattached)
                isattached "$@"
                ;;
	*)
		err "{\"status\": \"Failure\", \"message\": \"Unsupported command\"}"
		exit 1
esac

exit 1
